home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 1
/
Cream of the Crop 1.iso
/
PROGRAM
/
ULARN.ARJ
/
ULARN.TAR
/
ularn
/
shar.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-10-25
|
13KB
|
545 lines
/*
Shar puts readable text files together in a package
from which they are easy to extract. The original version
was a shell script posted to the net, shown below:
#Date: Mon Oct 18 11:08:34 1982
#From: decvax!microsof!uw-beave!jim (James Gosling at CMU)
AR=$1
shift
for i do
echo a - $i
echo "echo x - $i" >>$AR
echo "cat >$i <<'!Funky!Stuff!'" >>$AR
cat $i >>$AR
echo "!Funky!Stuff!" >>$AR
done
Rewritten by Gary Perlman/Wang Institute/Tyngsboro, MA/01879/(617) 649-9731
Many enhancements motivated by Michael Thompson.
Directory archiving motivated by Derek Zahn @ wisconsin
*/
/*
Expanded by Phil Cordier, philc@sco.com
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include <ctype.h>
/* COMMANDS */
#define EXTRACT "#! /bin/sh" /* magic exec string at shar file start */
#define PATH "/bin:$PATH" /* search path for programs */
#define CAT "cat"; /* /bin/cat */
#define SED "sed 's/^%s//'" /* /bin/sed removes Prefix from lines */
#define MKDIR "mkdir" /* make a new directory */
#define CHMOD "chmod " /* change file protection (for executables) */
#define CHDIR "cd" /* change current directory */
#define TEST "test" /* /bin/test files */
#define ECHO "echo" /* echo a message to extractor */
main (argc, argv)
int argc;
char **argv;
{
int shar ();
int optind;
if ((optind = initial (argc, argv)) < 0)
exit (1);
if (header (argc, argv, optind))
exit (2);
while (optind < argc)
traverse (argv[optind++], shar);
footer ();
exit (0);
}
/* OPTIONS */
int NOTEXT;
typedef int lgl;
#define true ((lgl) 1)
#define false ((lgl) 0)
int Lastchar; /* the last character printed */
int Ctrlcount; /* how many bad control characters are in file */
#define USAGE "[-abcsvz] [-p prefix] [-d delim] [-m M -n N] files > archive\n\
-a all of -bcvz\n\
-b extract into basenames\n\
-c count characters\n\
-s work silently\n\
-v verbose\n\
-z use original permissions\n\
-p prefix use <prefix> instead of X\n\
-p delim use <delim> as file delimiter\n\
-m M archive M of N\n\
-n N total # of archives"
#define OPTSTRING "abcsvzp:d:m:n:"
lgl Verbose = false; /* provide append/extract feedback */
lgl Basename = false; /* extract into basenames */
lgl Count = false; /* count characters to check transfer */
lgl Silent = false; /* turn off all verbosity */
lgl Saveperm = false; /* use original permissions */
char *Delim = "!FaR!OuT!"; /* put after each file */
char Filter[100] = CAT; /* used to extract archived files */
char *Prefix = NULL; /* line prefix to avoid funny chars */
char *M = NULL; /* archive M of N */
char *N = NULL; /* total # of archives */
/* returns the index of the first operand file */
initial (argc, argv)
int argc;
char **argv;
{
int errflg = 0;
extern int optind;
extern char *optarg;
int C;
NOTEXT=0;
while ((C = getopt (argc, argv, OPTSTRING)) != EOF)
switch (C) {
case 'v': Verbose = true; break;
case 'c': Count = true; break;
case 'b': Basename = true; break;
case 'd': Delim = optarg; break;
case 'z': Saveperm = true; break;
case 's': /* silent running */
Silent = true;
Verbose = false;
Count = false;
Prefix = NULL;
break;
case 'a': /* all the options */
Verbose = true;
Count = true;
Basename = true;
Saveperm = true;
/* fall through to set prefix */
optarg = "X";
case 'p': sprintf (Filter, SED, Prefix = optarg);
break;
case 'm': M = optarg;
break;
case 'n': N = optarg;
break;
default: errflg++;
}
if ( (M && !N) || (N && !M) ) {
fprintf(stderr, "Must give both -m and -n\n");
fflush(stderr);
exit(1);
}
if (!M && !N) {
M = "1";
N = "1";
}
if (M && N) {
if (atoi(M) <=0 || atoi(N)<=0) {
fprintf(stderr,
"Must give integer arguments to -m and -n\n");
fflush(stderr);
exit(1);
}
}
if (errflg || optind == argc) {
if (optind == argc)
fprintf (stderr, "shar: No input files\n");
fprintf (stderr, "usage: shar %s\n", USAGE);
fflush(stderr);
return (-1);
}
return (optind);
}
header (argc, argv, optind)
char **argv;
{
int i;
lgl problems = false;
long clock;
char *ctime ();
char *getenv ();
char *NAME = getenv ("NAME");
char *ORG = getenv ("ORGANIZATION");
for (i = optind; i < argc; i++)
if (access (argv[i], 4)) /* check read permission */ {
fprintf (stderr, "shar: Can't read '%s'\n", argv[i]);
fflush(stderr);
problems++;
}
if (problems)
return (problems);
puts (EXTRACT);
puts ("# This is a shell archive, meaning:");
printf ("# 1. Remove everything above the %s line.\n", EXTRACT);
puts ("# 2. Save the resulting text in a file.");
puts ("# 3. Execute the file with /bin/sh (not csh) to create the files:");
for (i = optind; i < argc; i++)
printf ("#\t%s\n", argv[i]);
time (&clock);
printf("#\n");
printf ("# This archive created: %s", ctime (&clock));
if (NAME)
printf ("# By:\t%s", NAME);
if (ORG)
printf (" (%s)", ORG);
putchar('\n');
puts("#");
printf ("export PATH; PATH=%s\n", PATH);
return (0);
}
footer ()
{
int i,n;
n = atoi(N);
puts ("#");
if (n > 1) {
printf ("#\tEnd of archive %s \(of %s\)\n", M, N);
printf ("cp /dev/null ark%sisdone\n", M);
puts ("MISSING=\"\"");
printf ("for I in ");
for (i=1; i<=n; i++)
printf("%d ",i);
puts(" ; do");
puts ("if test ! -f ark${I}isdone ; then");
puts (" MISSING=\"${MISSING} ${I}\"");
puts ("fi");
puts ("done");
puts ("if test \"${MISSING}\" = \"\" ; then");
if (Silent == false) {
if (n == 2)
printf (" %s You have unpacked both archives.\n",ECHO);
else
printf (" %s You have unpacked all %d archives.\n",
ECHO,n);
}
printf (" rm -f ark[1-9]isdone ");
if (n > 9)
puts ("ark[1-9][0-9]isdone");
else printf("\n");
if (Silent == false) {
puts ("else");
printf ("%s You still need to unpack the following archives:\n",
ECHO);
printf ("%s ${MISSING}\n",ECHO);
}
puts ("fi");
} /* end if n > 1 */
puts ("## End of shell archive.");
puts ("exit 0");
}
archive (input, output)
char *input, *output;
{
char buf[BUFSIZ];
FILE *ioptr;
if (ioptr = fopen (input, "r")) {
if (Count == true) {
Ctrlcount = 0; /* no bad control characters so far */
Lastchar = '\n'; /* simulate line start */
}
printf ("%s << \\%s > '%s'\n", Filter, Delim, output);
if (Prefix) {
while (fgets (buf, BUFSIZ, ioptr)) {
if (Prefix) outline (Prefix);
outline (buf);
}
}
else copyout (ioptr);
/* thanks to H. Morrow Long (ittvax!long) for the next fix */
if (Lastchar != '\n') /* incomplete last line */
putchar ('\n'); /* Delim MUST begin new line! */
puts (Delim);
if (Count == true && Lastchar != '\n')
printf ("%s A missing newline was added to \"'%s'\"\n", ECHO, input);
if (Count == true && Ctrlcount)
printf ("%s %d control %s may be missing from \"'%s'\"\n",
ECHO, Ctrlcount, (Ctrlcount == 1) ? "character" : "characters",
input);
(void) fclose (ioptr);
return (0);
}
else {
fprintf (stderr, "shar: Can't open '%s'\n", input);
fflush(stderr);
return (1);
}
}
/*
Copyout copies its ioptr almost as fast as possible
except that it has to keep track of the last character
printed. If the last character is not a newline, then
shar has to add one so that the end of file delimiter
is recognized by the shell. This checking costs about
a 10% difference in user time. Otherwise, it is about
as fast as cat. It also might count control characters.
*/
#define badctrl(c) (iscntrl (c) && !isspace (c))
copyout (ioptr)
register FILE *ioptr;
{
register int C;
register int L;
register count;
count = Count;
while ((C = getc (ioptr)) != EOF) {
if (count == true && badctrl (C)) Ctrlcount++;
L = putchar (C);
}
Lastchar = L;
}
outline (s)
register char *s;
{
if (*s) {
while (*s) {
if (Count == true && badctrl (*s)) Ctrlcount++;
putchar (*s++);
}
Lastchar = *(s-1);
}
}
#define FSIZE statbuf.st_size
shar (file, type, pos)
char *file; /* file or directory to be processed */
int type; /* either 'f' for file or 'd' for directory */
int pos; /* 0 going in to a file or dir, 1 going out */
{
struct stat statbuf;
char *basefile = file;
if (!strcmp (file, ".")) return;
if (stat (file, &statbuf)) FSIZE = 0;
if (type=='f' && pos==0 && !is_text(file)) {
fprintf(stderr,
"shar: %s is not a text file - not archived.\n",file);
NOTEXT=1;
fflush(stderr);
return;
}
if (Basename == true) {
while (*basefile) basefile++; /* go to end of name */
while (basefile > file && *(basefile-1) != '/') basefile--;
}
if (pos == 0) /* before the file starts */ {
if (type == 'd') {
printf ("if %s ! -d '%s'\n", TEST, basefile);
printf ("then\n");
if (Verbose == true)
printf (" %s Creating directory \"'%s'\"\n", ECHO, basefile);
printf (" %s '%s'\n", MKDIR, basefile);
if (Saveperm)
printf("%s 0%o %s\n",
CHMOD, statbuf.st_mode & 07777, basefile);
printf ("fi\n");
if (Verbose == true)
printf ("%s Entering directory \"'%s'\"\n", ECHO, basefile);
printf ("%s '%s'\n", CHDIR, basefile);
}
else /* type == 'f' */ {
if (Verbose == true)
printf ("%s Extracting \"'%s'\" '(%d characters)'\n",
ECHO, basefile, FSIZE);
if (Silent == false) {
printf ("if %s -f '%s'\n", TEST, basefile);
puts ("then");
printf (" %s Will not over-write existing file \"'%s'\"\n",
ECHO, basefile);
puts ("else");
}
if (archive (file, basefile)) exit (-1);
}
}
else /* pos == 1, after the file is archived */ {
if (type == 'd') {
if (Verbose == true)
printf ("%s Done with directory \"'%s'\"\n",
ECHO, basefile);
printf ("%s ..\n", CHDIR);
}
/* type == 'f' (plain file) */
else if (!NOTEXT) {
if (Count == true ) {
printf("if [ `wc -c %s | awk '{printf $1}'` -ne %d ]\nthen\n",
basefile, FSIZE);
printf("\t%s -n Size change in file %s: was %d, 'is '\n",
ECHO, basefile, FSIZE);
printf("\twc -c %s | awk '{printf $1}'\n", basefile);
printf("\t%s ' characters'\nfi\n",ECHO);
}
if (!Saveperm) {
/* if an executable file */
if (statbuf.st_mode & S_IEXEC
|| statbuf.st_mode & 010
|| statbuf.st_mode & 01) {
printf("%s ", CHMOD);
if (statbuf.st_mode & S_IEXEC)
putchar('u');
if (statbuf.st_mode & 010)
putchar('g');
if (statbuf.st_mode & 01)
putchar('o');
printf("+x %s\n", basefile);
} /* end if executable */
} else
printf("%s 0%o %s\n", CHMOD,
statbuf.st_mode & 07777, basefile);
#ifdef NEVER
/* as sent, as recieved */
if (Verbose == true ) {
char foo[BUFSIZ], line[BUFSIZ];
FILE *ioptr;
sprintf (line, "ls -l %s", file);
ioptr = popen (line, "r");
fgets(foo, BUFSIZ, ioptr);
pclose (ioptr);
foo[strlen(foo)-1] = '\0';
printf("%s \"As sent : %s\"\n", ECHO, foo);
printf("%s \"Received : `ls -l %s`\"\n",
ECHO,file);
} /* end if verbose */
#endif /* NEVER */
if (Silent == false)
puts ("fi # end of overwriting check");
}/* end plain file and if !NOTEXT */
NOTEXT=0;
} /* end else (pos == 1) */
}
#ifdef MAXNAMLEN
#define namedir(entry) (entry->d_name)
#define MAXNAME 256
#else
#define DIR FILE
#define MAXNAME (DIRSIZ+2)
#define opendir(path) fopen (path, "r")
#define closedir(dirp) fclose (dirp)
struct direct *
readdir (dirp)
DIR *dirp;
{
static struct direct entry;
if (dirp == NULL) return (NULL);
for (;;) {
if (fread (&entry, sizeof (struct direct), 1, dirp) == 0)
return (NULL);
if (entry.d_ino) return (&entry);
}
}
char *strncpy ();
char *
namedir (entry)
struct direct *entry;
{
static char name[MAXNAME];
return (strncpy (name, entry->d_name, DIRSIZ));
}
#endif MAXNAMLEN
#define isdir(path) (stat(path, &buf) ? 0 : (buf.st_mode&S_IFMT)==S_IFDIR)
traverse (path, func)
char *path;
int (*func) ();
{
DIR *dirp;
struct direct *entry;
struct stat buf;
int filetype = isdir (path) ? 'd' : 'f';
(*func) (path, filetype, 0);
if (filetype == 'd') {
if (chdir (path) == 0) {
if (dirp = opendir (".")) {
while (entry = readdir (dirp)) {
char name[MAXNAME];
strcpy (name, namedir (entry));
if (strcmp(name, ".") && strcmp(name, ".."))
traverse (name, func);
}
(void) closedir(dirp);
}
(void) chdir ("..");
}
}
(*func) (path, filetype, 1);
}
is_text (file)
char *file;
{
int count=0;
char line[BUFSIZ];
FILE *ioptr;
sprintf (line, "file %s | egrep -c \"text\"", file);
ioptr = popen (line, "r");
fscanf (ioptr, "%d", &count);
pclose (ioptr);
return (count);
}